Windows API ile bir tooltip kontrolü oluşturmak için CreateWindowEx() fonksiyonunun ikinci parametresinde TOOLTIPS_CLASS değerini kullanabiliriz. Aynı zamanda, bir Static kontrolü kullanarak kendimize özgü kullanışlı Tooltip kontrolleri oluşturabiliriz.
Bu amaçla, Static kontrol için alt sınıf oluşturma (Subclassing) yöntemiyle yeni bir fonksiyon oluşturarak, bu fonksiyon içinde Static kontrol boyama işlemlerini WM_PAINT mesaj seçeneği altında yapabiliriz.
1. Öncelikle Burada gösterildiği gibi bir Windows API projesi oluşturalım. Projeyle birlikte otomatik olarak oluşturulan main.c dosyasının başlangıç kısmını aşağıdaki şekilde düzenleyelim:
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
/* commctrl.h için */
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501
#define IDC_BUTTON01 101
#define IDC_BUTTON02 102
#define IDC_STATIC 103
#include <tchar.h>
#include <windows.h>
#include <commctrl.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları */
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
/* Diğer fonksiyon başlık tanımlamaları */
void OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);
BOOL g_MouseTracking;
/* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");
/* Buton ve Tooltip pencere oluşturma */
HWND hwndButton01, hwndButton02, hwndTooltip;
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
Aşağıdaki satır ile commctrl.h başlık dosyası ile alt sınıf oluşturma ile ilgili fonksiyonların ve değerlerin kullanımı sağlanır.
#include <commctrl.h>
Aşağıdaki satırlar ile Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları tanımlamaları programa eklenir.
CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
Aşağıdaki satırlar ile fare izlemeyi başlatan ve Static kontrol metin boyutlarını hesaplayan fonksiyon tanımlamaları programa eklenir.
OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);
Aşağıdaki satır ile fare izlemenin devrede aktif olup olmadığını gösteren g_MouseTracking global değişkeni tanımlanır.
BOOL g_MouseTracking;
Aşağıdaki satırlar ile iki adet Buton kontrolü ve bir adet Tooltip kontrolü oluşturulur.
HWND hwndButton01, hwndButton02, hwndToolTip;
WindowProcedure içinde oluşturacağımız WM_CREATE seçeneğine aşağıdaki satırları ekleyelim:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CREATE:
/* Buton oluşturma */
hwndButton01 = CreateWindowEx(0, "BUTTON", "Button01",
WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
hwndButton02 = CreateWindowEx(0, "BUTTON", "Button02",
WS_CHILD | WS_VISIBLE | BS_NOTIFY, 160, 20, 120, 25,
hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);
/* Tooltip için Static kontrol oluşturma */
hwndTooltip = CreateWindowEx(0, "STATIC", "İpucu",
WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOTIFY, 300, 20, 200, 20,
hwnd, (HMENU) IDC_STATIC, NULL, NULL);
/* Tooltip penceresini gizleme */
ShowWindow(hwndTooltip, SW_HIDE);
/* Alt sınıf (Subclassing) fonksiyonlarını atama */
SetWindowSubclass(hwndButton01, ButtonProc, 0, 0);
SetWindowSubclass(hwndButton02, ButtonProc, 0, 0);
SetWindowSubclass(hwndTooltip, StaticProc, 0, 0);
break;
Önce iki adet Button kontrolü ve bir adet Tooltip için kullanılacak Static kontrol oluşturulur.
Static kontrol penceresi gizlenir.
SetWindowSubclass() fonksiyonu kullanılarak, Button kontrolleri için ButtonProc() fonksiyonu, Static kontrol için StaticProc() fonksiyonu alt sınıf fonksiyonu olarak atanır.
Tooltip olarak kullanacağımız Static kontrolü için alt sınıf fonksiyonunu oluşturarak programa ekleyelim:
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
HPEN hPen;
HFONT hFont;
RECT rc;
char cdizi[100];
/* Pencere metin içeriğini alma */
GetWindowText(hwnd, cdizi, sizeof(cdizi));
hdc = BeginPaint(hwnd, &ps);
SetBkMode(hdc, TRANSPARENT);
GetClientRect(hwnd, &rc);
hPen = CreatePen(PS_SOLID, 0, RGB(244, 244, 244)); /* Çerçeve rengi */
SelectObject(hdc, hPen);
hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
SelectObject(hdc, hFont);
SelectObject(hdc, GetStockObject(DC_BRUSH));
SetDCBrushColor(hdc, RGB(165, 226, 165)); /* Arka plan rengi */
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); /* Dikdörtgen çizer ve içini doldurur. */
DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); /* Metin içeriğini yazar. */
EndPaint(hwnd, &ps);
return 0L;
}
break;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
Static kontrol ile ilgili tüm boyama işlemleri StaticProc() fonksiyonu içinde WM_PAINT mesajı ile yapılır.
Button kontrolleri için alt sınıf fonksiyonunu oluşturarak programa ekleyelim:
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_MOUSEMOVE: /* TrackMouseEvent() gerekmez */
{
POINT p;
RECT rcButton, rcTooltip;
char cdizi[100];
/* Eğer fare izleme devre dışı ise, izlemeyi başlatma */
if (!g_MouseTracking) OnMouseMove(hwnd);
if (!IsWindowVisible(hwndTooltip)) {
GetWindowText(hwnd, cdizi, sizeof(cdizi));
if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
SetWindowText(hwndTooltip, cdizi);
GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
/* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
rcTooltip = CalcTextDim (hwndTooltip);
/* Tooltip penceresinin yerini ve boyutunu ayarlama */
MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
ShowWindow(hwndTooltip, SW_SHOW);
}
return 0;
}
case WM_MOUSELEAVE: /* TrackMouseEvent() gerekir */
ShowWindow(hwndTooltip, SW_HIDE);
g_MouseTracking = FALSE;
return 0;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
ButtonProc() fonksiyonu içinde WM_MOUSEMOVE ve WM_MOUSELEAVE mesajlarına işlem yapılarak Tooltip olarak kullanılan Static kontrolün farenin üzerinde bulunduğu butonun değerlerine göre ekran da gösterilmesi ya da gizlenmesi sağlanır.
Bir alt sınıf fonksiyonunda WM_MOUSEMOVE mesajı TrackMouseEvent() fonksiyonu çağrılmadan alınabilir ve işlem yapılabilir. Ancak, WM_MOUSELEAVE mesajının alınmakı ve işlem yapılabilmesi için TrackMouseEvent() fonksiyonunun çağrılarak fare hareketlerinin izlenmesi gerekir.
WM_MOUSEMOVE mesajı alındığında, yani fare buton kontrollerinden herhangi bir üzerinde hareket ettiğinde, eğer g_MouseTracking global değişkeni FALSE bir değer taşıyorsa, OnMouseMove() fonksiyonu çağrılarak TrackMouseEvent() fonksiyonu farenin üzerinde olduğu Buton kontrolü pencere değeri ile çalıştırılır.
Eğer Tooltip penceresi (Static kontrol) görünür değilse,
WM_MOUSELEAVE mesajı alındığında, yani fare buton kontrollerinden herhangi bir üzerinden ayrıldığında, ShowWindow() fonksiyonu ile Static kontrol gizlenir ve g_MouseTracking global değişkenine FALSE değeri atanır.
Eğer aşağıdaki ilk ve son satırı devre dışı bırakır, yani IsWindowVisible() fonksiyonunu çalıştırmazsanız, fare buton kontrollerinden herhangi biri üzerinde gezinirken, Tooltip penceresi sürekli hareket eder.
// if (!IsWindowVisible(hwndTooltip)) {
GetWindowText(hwnd, cdizi, sizeof(cdizi));
if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
SetWindowText(hwndTooltip, cdizi);
GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
/* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
rcTooltip = CalcTextDim (hwndTooltip);
/* Tooltip penceresinin yerini ve boyutunu ayarlama */
MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
ShowWindow(hwndTooltip, SW_SHOW);
// }
Fare izlemeyi aktif hale getiren fonksiyonu oluşturarak programa ekleyelim:
void OnMouseMove(HWND hwnd)
{
/* Mouse izlemeyi devreye sokma */
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_LEAVE;
TrackMouseEvent(&tme);
g_MouseTracking = TRUE;
}
Static kontrol metin boyutlarına göre kontrolün genişliğini ve yüksekliğini hesaplayan fonksiyonu oluşturarak programa ekleyelim:
RECT CalcTextDim (HWND hwnd)
{
HDC hdc;
HFONT hFont;
RECT rc = {0};
char cdizi[100];
/* Pencere metin içeriğini alma */
GetWindowText(hwnd, cdizi, sizeof(cdizi));
hdc = GetDC(hwnd);
SetBkMode(hdc, TRANSPARENT);
hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
SelectObject(hdc, hFont);
/* Metin içeriğine göre çerçevenin genişliğini ve yüksekliğini hesaplama */
DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CALCRECT);
ReleaseDC(hwnd, hdc);
return rc;
}
Yukarıdaki kodları eklediğimizde, main.c dosyasının en son hali aşağıdaki şekilde olacaktır.
main.c
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
/* commctrl.h için */
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501
#define IDC_BUTTON01 101
#define IDC_BUTTON02 102
#define IDC_STATIC 103
#include <tchar.h>
#include <windows.h>
#include <commctrl.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Static ve Button kontrolleri için alt sınıf (Subclassing) fonksiyonları */
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
/* Diğer fonksiyon başlık tanımlamaları */
void OnMouseMove(HWND hwnd);
RECT CalcTextDim (HWND hwnd);
BOOL g_MouseTracking;
/* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");
/* Buton ve Tooltip pencere oluşturma */
HWND hwndButton01, hwndButton02, hwndTooltip;
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
_T("Tooltip kontrolü oluşturma"), /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
544, /* The programs width */
385, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CREATE:
/* Buton oluşturma */
hwndButton01 = CreateWindowEx(0, "BUTTON", "Button01",
WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
hwndButton02 = CreateWindowEx(0, "BUTTON", "Button02",
WS_CHILD | WS_VISIBLE | BS_NOTIFY, 160, 20, 120, 25,
hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);
/* Tooltip için Static kontrol oluşturma */
hwndTooltip = CreateWindowEx(0, "STATIC", "İpucu",
WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOTIFY, 300, 20, 200, 20,
hwnd, (HMENU) IDC_STATIC, NULL, NULL);
/* Tooltip penceresini gizleme */
ShowWindow(hwndTooltip, SW_HIDE);
/* Alt sınıf (Subclassing) fonksiyonlarını atama */
SetWindowSubclass(hwndButton01, ButtonProc, 0, 0);
SetWindowSubclass(hwndButton02, ButtonProc, 0, 0);
SetWindowSubclass(hwndTooltip, StaticProc, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK StaticProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
HPEN hPen;
HFONT hFont;
RECT rc;
char cdizi[100];
/* Pencere metin içeriğini alma */
GetWindowText(hwnd, cdizi, sizeof(cdizi));
hdc = BeginPaint(hwnd, &ps);
SetBkMode(hdc, TRANSPARENT);
GetClientRect(hwnd, &rc);
hPen = CreatePen(PS_SOLID, 0, RGB(244, 244, 244)); /* Çerçeve rengi */
SelectObject(hdc, hPen);
hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
SelectObject(hdc, hFont);
SelectObject(hdc, GetStockObject(DC_BRUSH));
SetDCBrushColor(hdc, RGB(165, 226, 165)); /* Arka plan rengi */
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); /* Dikdörtgen çizer ve içini doldurur. */
DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); /* Metin içeriğini yazar. */
EndPaint(hwnd, &ps);
return 0L;
}
break;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK ButtonProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_MOUSEMOVE: /* TrackMouseEvent() gerekmez */
{
POINT p;
RECT rcButton, rcTooltip;
char cdizi[100];
/* Eğer fare izleme devre dışı ise, izlemeyi başlatma */
if (!g_MouseTracking) OnMouseMove(hwnd);
if (!IsWindowVisible(hwndTooltip)) {
GetWindowText(hwnd, cdizi, sizeof(cdizi));
if (hwnd==hwndButton02) strcat(cdizi, " ipucu");
SetWindowText(hwndTooltip, cdizi);
GetCursorPos(&p); /* Ekrana göre cursor koordinatları */
ScreenToClient(hwnd, &p); /* Kontrola göre cursor koordinatları */
GetClientRect(hwnd,&rcButton); /* Buton pencere genişlik ve yüksekliği */
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rcButton, 2); /* Butonun ana pencere üzerindeki koordinatları */
/* Tooltip penceresinin metin uzunluğuna göre pencere boyutunu hesaplama */
rcTooltip = CalcTextDim (hwndTooltip);
/* Tooltip penceresinin yerini ve boyutunu ayarlama */
MoveWindow(hwndTooltip, p.x+rcButton.left, rcButton.bottom+5, rcTooltip.right+10, rcTooltip.bottom+4, TRUE);
ShowWindow(hwndTooltip, SW_SHOW);
}
return 0;
}
case WM_MOUSELEAVE: /* TrackMouseEvent() gerekir */
ShowWindow(hwndTooltip, SW_HIDE);
g_MouseTracking = FALSE;
return 0;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
void OnMouseMove(HWND hwnd)
{
/* Mouse izlemeyi devreye sokma */
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_LEAVE;
TrackMouseEvent(&tme);
g_MouseTracking = TRUE;
}
RECT CalcTextDim (HWND hwnd)
{
HDC hdc;
HFONT hFont;
RECT rc = {0};
char cdizi[100];
/* Pencere metin içeriğini alma */
GetWindowText(hwnd, cdizi, sizeof(cdizi));
hdc = GetDC(hwnd);
SetBkMode(hdc, TRANSPARENT);
hFont = CreateFont(13, 0, 0, 0, FW_NORMAL, 0, 0, 0, TURKISH_CHARSET, 0, 0, 0, 0, TEXT("Tahoma"));
SelectObject(hdc, hFont);
/* Metin içeriğine göre çerçevenin genişliğini ve yüksekliğini hesaplama */
DrawText(hdc, cdizi, strlen(cdizi), &rc, DT_CALCRECT);
ReleaseDC(hwnd, hdc);
return rc;
}
Program derleyip çalıştırdığımızda aşağıdakine benzer bir ekran görüntüsü karşımıza gelecektir. Fareyi buton kontrolünün üzerine getirdiğimizde ipucu penceresi ekranda görünecek, fareyi buton kontrolü üzerinden çektiğimizde ipucu penceresi kaybolacaktır.